package com.daifeng.gulimall.search.services.Impl;

import com.alibaba.fastjson.JSON;
import com.daifeng.common.to.es.SkuEsModel;
import com.daifeng.gulimall.search.constant.Esconstant;
import com.daifeng.gulimall.search.services.MallSerchService;
import com.daifeng.gulimall.search.vo.SearchPamarVo;
import com.daifeng.gulimall.search.vo.SerachResults;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class MallSerchServiceImpl implements MallSerchService {

    @Resource(name = "restHighLevelClient")
    private RestHighLevelClient client;

    @Override
    public SerachResults search(SearchPamarVo pamarVo) {

        //获取查询请求  参数：  查询的索引的名(类似数据库的名字)
        SearchRequest request =sourceBuilderRequest(pamarVo);
        // 构建搜索条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        SerachResults result = null;

        try {
            //  执行检索
            SearchResponse searchResponse = client.search(request,RequestOptions.DEFAULT);

            result = SearchSourceBuilderResult(searchResponse,pamarVo);

        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }


    /**
     * 封装结果集
     * @return
     */
    private SerachResults SearchSourceBuilderResult(SearchResponse response,SearchPamarVo param) {
        SerachResults result = new SerachResults();

        // 1.   封装符合条件的商品数据
        List<SkuEsModel> esModelList  = new ArrayList<>();
        SearchHits hits = response.getHits();
        if(hits.getHits() != null && hits.getHits().length>0){

            for (SearchHit hit : hits.getHits()) {
                String sourceAsString = hit.getSourceAsString();
                //  将json字符串装换成实体类对象
                SkuEsModel model = JSON.parseObject(sourceAsString,SkuEsModel.class);
                if(!StringUtils.isEmpty(param.getKeyword())){
                    //  获取高亮字段的信息
                    HighlightField title = hit.getHighlightFields().get("skuTitle");
                    model.setSkuTitle(title.getFragments()[0].toString());
                }
                esModelList.add(model);
            }

        }
        result.setEsModelList(esModelList);

        // 2.  当前商品涉及到的 品牌
        //  通过聚合的名字 获取当前聚合的对象
        ParsedLongTerms brandAagg = response.getAggregations().get("brand_agg");
        List<SerachResults.brandVo> brandVos = new ArrayList<>();
        List<? extends Terms.Bucket> buckets = brandAagg.getBuckets();
        for (Terms.Bucket bucket : buckets) {
            SerachResults.brandVo brandVo = new SerachResults.brandVo();
            // 品牌id
            brandVo.setBrandId(bucket.getKeyAsNumber().longValue());
            //2、得到品牌的名
            String brandName = ((ParsedStringTerms) bucket.getAggregations().get("brand_name_agg")).getBuckets().get(0).getKeyAsString();
            //3、得到品牌的图片
            String brandImg = ((ParsedStringTerms) bucket.getAggregations().get("brand_img_agg")).getBuckets().get(0).getKeyAsString();
            brandVo.setBrandName(brandName);
            brandVo.setImg(brandImg);
            brandVos.add(brandVo);
        }
         result.setBrands(brandVos);


        // 3.  当前商品涉及到的所有的分类信息

        ParsedLongTerms catalog_agg = response.getAggregations().get("catalog_agg");
        List<SerachResults.catalogVo> catalogVos = new ArrayList<>();
        List<? extends Terms.Bucket> catalogbuckets = catalog_agg.getBuckets();
        for (Terms.Bucket bucket : catalogbuckets) {
            SerachResults.catalogVo catalogVo = new SerachResults.catalogVo();
            //得到分类id
            String keyAsString = bucket.getKeyAsString();
            catalogVo.setCatId(Long.parseLong(keyAsString));
            //得到分类名
            ParsedStringTerms catalog_name_agg = bucket.getAggregations().get("catalog_name_agg");
            String catalog_name = catalog_name_agg.getBuckets().get(0).getKeyAsString();
            catalogVo.setCatName(catalog_name);
            catalogVos.add(catalogVo);
        }
        result.setCatalogs(catalogVos);

        // 4 .当前商品涉及到的所有属性信息

        List<SerachResults.attrVo> attrVos = new ArrayList<>();
        ParsedNested attr_agg = response.getAggregations().get("attr_agg");
        ParsedLongTerms attr_id_agg = attr_agg.getAggregations().get("attr_id_agg");
        for (Terms.Bucket bucket : attr_id_agg.getBuckets()) {
            SerachResults.attrVo attrVo = new SerachResults.attrVo();
            //1、得到属性的id;
            long attrId = bucket.getKeyAsNumber().longValue();
            //2、得到属性的名字
            String attrName = ((ParsedStringTerms) bucket.getAggregations().get("attr_name_agg")).getBuckets().get(0).getKeyAsString();
            //3、得到属性的所有值
            List<String> attrValues = ((ParsedStringTerms) bucket.getAggregations().get("attr_value_agg")).getBuckets().stream().map(item -> {
                String keyAsString = item.getKeyAsString();
                return keyAsString;

            }).collect(Collectors.toList());
            attrVo.setAttrId(attrId);
            attrVo.setAttrName(attrName);
            attrVo.setAttrValues(attrValues);
            attrVos.add(attrVo);
        }
        result.setAttrs(attrVos);

        // 5. 分页信息
          result.setPageNum(param.getPageNo());
          result.setTotal(hits.getTotalHits().value);    //  总记录数
          Long num = hits.getTotalHits().value%Esconstant.PAGESIEZ==0?hits.getTotalHits().value/Esconstant.PAGESIEZ:(hits.getTotalHits().value/Esconstant.PAGESIEZ)+1;
          result.setTotalPages(num);  // 总页数
          List<Integer> list = new ArrayList<>();
            for (int i = 0; i < num ; i++) {
                list.add(i+1);
            }
            result.setTotalList(list);
//        System.out.println(hits.getTotalHits().value/Esconstant.PAGESIEZ);
//        System.out.println(result.getTotalPages()+"总页数+++++++");
        return result;
    }

    /**
     * 构建查询条件
     * 模糊配置 过滤（按照属性，分类，品牌，价格区间，库存）， 排序 ，分页 ，高亮, 聚合
     * @param pamarVo
     * @return
     */
    private SearchRequest sourceBuilderRequest(SearchPamarVo pamarVo) {

        // 准备dsl语句
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        /**
         * 过滤（按照属性，，品牌，价格区间，库存）
         */
        //  1 构建bool
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 1.1 must-模糊匹配
        if(!StringUtils.isEmpty(pamarVo.getKeyword())){
            boolQuery.must(QueryBuilders.matchQuery("skuTitle",pamarVo.getKeyword()));
        }

        // 1.2  过滤  filter
        if(pamarVo.getCatalogId() != null){
            //  1.2.1 按照三级分类id类查询
            boolQuery.filter(QueryBuilders.termQuery("catalogId",pamarVo.getCatalogId()));
        }
        if(pamarVo.getBrandId() != null && pamarVo.getBrandId().size() > 0){
            // 1.2.2  按照品牌id来查询  brandId
            boolQuery.filter(QueryBuilders.termsQuery("brandId",pamarVo.getBrandId()));
        }

        if(pamarVo.getStock() != null){
            // 1.2.2  库存stock 1为有货  0为无货
            boolQuery.filter(QueryBuilders.termsQuery("hasStock",pamarVo.getStock()==1?true:false));
        }

        /**
         * 1.2.3
         * 价格区间：price=0_400 或者 price=_200 或者 price=200_
         * 参数:要查询的列名
         */
        if(!StringUtils.isEmpty(pamarVo.getPrice())){
            String price = pamarVo.getPrice();
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("skuPrice");
            String[] split = price.split("_");
            if(split.length ==2){
                rangeQuery.gte(split[0]).lte(split[1]);
            }else if(split.length == 1){
                if (price.startsWith("_")){  //判断price 是否已_开头的  是则返回true
                    rangeQuery.lte(split[0]);
                }else {
                    rangeQuery.gte(split[0]);
                }
            }
            boolQuery.filter(rangeQuery);
        }
        /**
         *  attrs  按属性查询
         *  attrs=1_3G:4G；
         */
        if(pamarVo.getAttrs() != null && pamarVo.getAttrs().size() > 0){
            pamarVo.getAttrs().forEach(item ->{
                BoolQueryBuilder nestedBuilder = QueryBuilders.boolQuery();
                String[] s = item.split("_");
                String attrid = s[0];  //属性id
                String[] attrValues = s[1].split(":");
                nestedBuilder.must(QueryBuilders.termQuery("attrs.attrId",attrid));
                nestedBuilder.must(QueryBuilders.termQuery("attrs.attrValue",s[1]));
                NestedQueryBuilder nestedQueryBuilder = new NestedQueryBuilder("attrs",nestedBuilder,ScoreMode.None);
                boolQuery.filter(nestedQueryBuilder);
            });
        }

        // 构建bool
        sourceBuilder.query(boolQuery);

        /**
         *  排序 ，分页 ，高亮,
         */
        //  sort
        //排序：sort=price/salecount/hotscore_desc/asc
        if(!StringUtils.isEmpty(pamarVo.getSort())){
            String[] sort = pamarVo.getSort().split("_");
            SortOrder order = sort[1].equalsIgnoreCase("asc")?SortOrder.ASC:SortOrder.DESC;
            sourceBuilder.sort(sort[0],order);
        }
        // 分页

        sourceBuilder.from((int) ((pamarVo.getPageNo()-1)*Esconstant.PAGESIEZ));

        // TODO pageSize
        if(pamarVo.getPageSize() != null){
            sourceBuilder.size(Integer.parseInt(pamarVo.getPageSize().toString()));
        }else {
            sourceBuilder.size(Esconstant.PAGESIEZ);
        }

        // 高亮
        if(!StringUtils.isEmpty(pamarVo.getKeyword())){
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
            highlightBuilder.preTags("<b style='color:red'>");
            highlightBuilder.postTags("</b>");
            sourceBuilder.highlighter(highlightBuilder);
        }

        /**
         * 聚合
         */
       //5.1 按照品牌聚合
        TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg").field("brandId").size(50);
        //品牌聚合的子聚合
        TermsAggregationBuilder brand_name_agg = AggregationBuilders.terms("brand_name_agg").field("brandName").size(1);
        TermsAggregationBuilder brand_img_agg = AggregationBuilders.terms("brand_img_agg").field("brandImg");
        brand_agg.subAggregation(brand_name_agg);
        brand_agg.subAggregation(brand_img_agg);
        sourceBuilder.aggregation(brand_agg);

        //5.2 按照catalog聚合
        TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg").field("catalogId").size(20);
        TermsAggregationBuilder catalog_name_agg = AggregationBuilders.terms("catalog_name_agg").field("catalogName").size(1);
        catalog_agg.subAggregation(catalog_name_agg);
        sourceBuilder.aggregation(catalog_agg);

        //5.3 按照attrs聚合
        NestedAggregationBuilder nestedAggregationBuilder = new NestedAggregationBuilder("attr_agg", "attrs");

        //按照attrId聚合
        TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId");

        //按照attrId聚合之后再按照attrName和attrValue聚合
        TermsAggregationBuilder attr_name_agg = AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1);
        TermsAggregationBuilder attr_value_agg = AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50);
        attr_id_agg.subAggregation(attr_name_agg);
        attr_id_agg.subAggregation(attr_value_agg);
        nestedAggregationBuilder.subAggregation(attr_id_agg);
        sourceBuilder.aggregation(nestedAggregationBuilder);

        System.out.println("检索的dcl语句："+sourceBuilder.toString());
        return  new SearchRequest(new String[]{Esconstant.PRODUCT_INDEX},sourceBuilder);
    }
}
